/**
 * @file
 * Custom locale implementation.
 *
 * The country-language pairs are generated using Qt's own locale system
 * and no guarantees are made about it's completeness or correctness.
 *
 * Copyright (c) 2009 by Pete Black <theblackpeter@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * @author Pete Black <theblackpeter@gmail.com>
 */
#include <QCoreApplication>
#include <QFile>
#include <QMap>
#include <QStringList>
#include <QtDebug>

#include "kisalocale.h"

/**
 * Instances of this class will have this name.
 *
 * @note this is not a unique identifier, just a common name.
 */
#ifndef OBJECT_NAME
#define OBJECT_NAME "kisalocale"
#endif

/**
 * Name used when outputting debug information.
 */
#ifndef OBJECT_DEBUG_NAME
#define OBJECT_DEBUG_NAME "kisalocale:"
#endif

// TODO: use Q_GLOBAL_STATIC here instead?
// http://delta.affinix.com/2006/01/31/q_global_static
QMap<QString, QString> KisaLocale::countryToLanguageMap = QMap<QString, QString>();
QMap<QString, QString> KisaLocale::keyboardLayoutToLanguageMap = QMap<QString, QString>();

KisaLocale::KisaLocale() {
  if(countryToLanguageMap.isEmpty()) {
    loadDefaultCountryToLanguageMap();
    updateOverrideMaps();
  }

  qDebug() << OBJECT_DEBUG_NAME << OBJECT_NAME << "created";
}

KisaLocale::~KisaLocale() {
  qDebug() << OBJECT_DEBUG_NAME << OBJECT_NAME << "destroyed";
}

const QString KisaLocale::getLanguageFromCountryCode(const QString& countryCode) {
  QString country = countryCode.toLower();

  if(countryToLanguageMap.contains(country))
    // the list from values is the most recently inserted to the least recently
    // inserted and we want the "first"
    return countryToLanguageMap.values(country).last();

  qDebug() << OBJECT_DEBUG_NAME << "no language found for country code" << country;
  return QString();
}

const QLocale KisaLocale::getLocaleFromKeyboardLayout(const QString& keyboardLayout) {
  if(keyboardLayout.isEmpty())
      return QLocale::c();

  if(keyboardLayoutToLanguageMap.contains(keyboardLayout)) {
    QString languageCode = keyboardLayoutToLanguageMap[keyboardLayout];
    return QLocale(languageCode);
  }

  return QLocale::c();
}

const QLocale KisaLocale::guessLocaleFromLanguageCode(const QString& languageCode) {
  if(!languageCode.isEmpty()) {
    return QLocale(languageCode);
  }

  qDebug() << OBJECT_DEBUG_NAME << "no locale found for language code" << languageCode;
  return QLocale::c();
}

const QLocale KisaLocale::guessLocaleFromCountryCode(const QString& countryCode) {
  QString language = getLanguageFromCountryCode(countryCode);

  if(!language.isEmpty()) {
    QString localeCode = language + "_" + countryCode.toUpper();
    return QLocale(localeCode);
  }

  qDebug() << OBJECT_DEBUG_NAME << "no locale found for country code" << countryCode << "with language" << language;
  return QLocale::c();
}

void KisaLocale::loadDefaultCountryToLanguageMap() {
  qDebug() << OBJECT_DEBUG_NAME << "generating default countryToLanguageMap...";

  // iterate over all the languages and add the first language to the
  // corresponding country
  for(int i = 0; i < (int) QLocale::LastLanguage; i++) {
    QLocale::Language language = (QLocale::Language) i;
    QList<QLocale::Country> countries = QLocale::countriesForLanguage(language);

    foreach(QLocale::Country country, countries) {
        QLocale locale(language, country);

		// QLocale uses the two-character ISO code formats: language_COUNTRY
        QString countryCode = locale.name().right(2).toLower();
        QString languageCode = locale.name().left(2);

        if(!countryToLanguageMap.contains(countryCode))
          countryToLanguageMap[countryCode] = languageCode;
      }
  }

  qDebug() << OBJECT_DEBUG_NAME << "iterated over" << (int) QLocale::LastLanguage << "languages";
  qDebug() << OBJECT_DEBUG_NAME << "added default values for" << countryToLanguageMap.keys().size() << "countries";
}

void KisaLocale::updateOverrideMaps() {
  qDebug() << OBJECT_DEBUG_NAME << "reading mappings_override.txt for overrides";

  QString filepath = QCoreApplication::applicationDirPath();
  // remove the "bin" folder at the end and add the end
  filepath.chop(3);
  filepath += "share/kisa/";
  QString fileName = filepath + "mappings_override.txt";
  QFile file(fileName);

  if(!file.exists()) {
    qDebug() << OBJECT_DEBUG_NAME << "no" << fileName << "found, nothing updated";
    return;
  }

  if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QTextStream textStream(&file);
    while(!textStream.atEnd()) {
      QString line = textStream.readLine();
      if(!line.startsWith("#")) {
        // remove any comments
        line = line.split("#").first();
        if(!line.isEmpty()) {
          QString countryCode = line.split("=").first().trimmed();
          QString languageCode = line.split("=").last().trimmed();

          if(countryCode.length() == 2 && languageCode.length() == 2) {
            countryToLanguageMap[countryCode] = languageCode;
            qDebug() << OBJECT_DEBUG_NAME << "updated country code" << countryCode << "with language code" << languageCode;
          }
          else if(languageCode.length() == 5) {
            keyboardLayoutToLanguageMap[countryCode] = languageCode;
            qDebug() << OBJECT_DEBUG_NAME << "added layout override for" << countryCode << "to language code" << languageCode;
          }
        }
      }
    }
  }
  else {
    qDebug() << OBJECT_DEBUG_NAME << "could not open" << fileName << "no values updated";
  }
}
